class Annotate {
	static activeTool = 1;
	static undoOperation = [];
	static redoOperation = [];
	static color = '#212121';
	static borderColor = '#000000';
	static fillColor = 'rgba(0, 0, 0, 0)';
	static borderSize = 1;
	static fontSize = 16;
	static width = 0;
	static height = 0;
	static scale = 1;
	static oldLeft = -1;
	static oldTop = -1;
	static commentLeft = -1;
	static commentTop = -1;
	static canvas = null;
	static mouseFrom = {};
	static mouseTo = {};
	static moveCount = 1;
	static doDrawing = false;
	static drawingObject = null;
	static mouseOutFlag = false;
	static undo = true;
	static brushs = {};
	static storages = {};
	static messages = [];
	static processingMessage = false;
	static bordersAndControlsOptions = {
		padding: 3,
		borderScaleFactor: 3,
		cornerSize: 10,
		cornerBackgroundColor: 'black',
		borderColor: '#00BFFF',
		cornerColor: '#00BFFF',
		cornerStyle: 'circle',
		transparentCorners: true
	};
	static hasBordersAndControls = true;
	static ActiveTool = {
		Selector: 0,
		Pencil: 1,
		Text: 2,
		Rectangle: 3,
		Circle: 4,
		Lead: 5,
		Image: 6
	};
	constructor(id, copyFrom, page) {
		let canvas = new fabric.Canvas(id, {
			enablePointerEvents: true,
			freeDrawingBrush: {
				width: 1,
				color: Annotate.color
			}
		});
		Annotate.canvas = canvas;
		Annotate.page = page;
		this.canvas = canvas;
		canvas.selection = false;
		canvas.on('object:added', e => {
			let target = e.target;
			if (target.annotationId) {
				if (Annotate.undo) {
					Annotate.addUndo({
						id: target.annotationId,
						before: null,
						after: Annotate.toJSON(target)
					});
				}
				Annotate.undo = true;
			}
		});

		canvas.on('object:rotating', e => {
			let obj = e.target;
			Annotate.notifyUpdateItem({
				annotationId: obj.annotationId,
				annotationType: obj.annotationType,
				angle: obj.angle,
				left: obj.left,
				top: obj.top
			});
			Annotate.notifyLatestItem(obj);
		});

		canvas.on('object:scaling', e => {
			let obj = e.target;
			obj.strokeUniform = true;
			if (!obj.strokeWidthUnscaled && obj.strokeWidth) {
				obj.strokeWidthUnscaled = obj.strokeWidth;
			}
			if (obj.strokeWidthUnscaled) {
				obj.strokeWidth = obj.strokeWidthUnscaled;
			}
			Annotate.notifyUpdateItem({
				annotationId: obj.annotationId,
				annotationType: obj.annotationType,
				scaleX: obj.scaleX,
				scaleY: obj.scaleY,
				strokeWidth: obj.strokeWidth,
				strokeWidthUnscaled: obj.strokeWidthUnscaled,
				strokeUniform: true
			});
			Annotate.notifyLatestItem(obj);
			Annotate.createControlPanel(canvas, obj);
		});
		canvas.on('object:removed', () => {
			$('.controlPanel').remove();
			Annotate.updateUndoRedoCss();
		});
		canvas.on('object:moving', e => {
			let obj = e.target;
			Annotate.createControlPanel(canvas, obj);
			Annotate.movingComment(obj);
			Annotate.notifyUpdateItem({
				annotationId: obj.annotationId,
				annotationType: obj.annotationType,
				left: obj.left,
				top: obj.top
			});
			Annotate.notifyLatestItem(obj);
		});

		canvas.on('selection:created', e => {
			e.selected.forEach(selected => {
				Annotate.createControlPanel(canvas, selected);
				$('.conmentPanel').remove();
				Annotate.activeTool = Annotate.ActiveTool.Selector;
				Annotate.activeToolButton(Annotate.ActiveTool.Selector);

				if (
					selected.annotationType === 'lead' ||
					selected.annotationType === 'path' ||
					selected.annotationType === 'rect' ||
					selected.annotationType === 'image' ||
					selected.annotationType === 'circle'
				) {
					$.each(Annotate.canvas.getObjects(), (index, object) => {
						if ('comment' + selected.annotationId === object.annotationId) {
							Annotate.oldLeft = selected.left;
							Annotate.oldTop = selected.top;
							Annotate.commentLeft = object.left;
							Annotate.commentTop = object.top;
							return false;
						}
					});
				}
			});
		});
		canvas.on('selection:updated', e => {
			e.selected.forEach(selected => {
				Annotate.createControlPanel(canvas, selected);
				$('.conmentPanel').remove();
				Annotate.activeTool = Annotate.ActiveTool.Selector;
				Annotate.activeToolButton(Annotate.ActiveTool.Selector);
				if (
					selected.annotationType === 'lead' ||
					selected.annotationType === 'path' ||
					selected.annotationType === 'rect' ||
					selected.annotationType === 'image' ||
					selected.annotationType === 'circle'
				) {
					$.each(Annotate.canvas.getObjects(), (index, object) => {
						if ('comment' + selected.annotationId === object.annotationId) {
							Annotate.oldLeft = selected.left;
							Annotate.oldTop = selected.top;
							Annotate.commentLeft = object.left;
							Annotate.commentTop = object.top;
							return false;
						}
					});
				}
			});
		});
		canvas.on('selection:cleared', () => {
			$('.controlPanel').remove();
		});
		let before = null;
		canvas.on('mouse:down', options => {
			if (Annotate.activeTool === Annotate.ActiveTool.Selector) {
				before = Annotate.toJSON(Annotate.canvas.getActiveObject());
			}
			let xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
			Annotate.mouseFrom.x = xy.x;
			Annotate.mouseFrom.y = xy.y;
			Annotate.updateActiveTool(Annotate.mouseFrom.x, Annotate.mouseFrom.y);
			Annotate.doDrawing = true;
			if (canvas.isDrawingMode) {
				Annotate.penId = Annotate.uuid();
				Annotate.notifyPenEvent({
					annotationId: Annotate.penId,
					point: { x: xy.x, y: xy.y },
					event: 'mousedown',
					color: Annotate.color,
					borderSize: Annotate.borderSize
				});
			}

			$.each(canvas.getObjects(), (index, object) => {
				if (object.annotationType === 'text') {
					if (Annotate.mouseOutFlag === true) {
						Annotate.mouseOutFlag = false;
						object.exitEditing();
					}
				}
				canvas.requestRenderAll();
			});
		});
		canvas.on('mouse:move', options => {
			if (!Annotate.doDrawing) {
				return;
			}
			let xy;
			if (canvas.isDrawingMode) {
				xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
				Annotate.notifyPenEvent({
					annotationId: Annotate.penId,
					point: { x: xy.x, y: xy.y },
					event: 'mousemove'
				});
				return;
			}
			Annotate.moveCount++;
			if (Annotate.moveCount % 10 === 0) {
				xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
				Annotate.mouseTo.x = xy.x;
				Annotate.mouseTo.y = xy.y;
				Annotate.drawing();
			}
		});
		canvas.on('mouse:up', options => {
			let after = Annotate.toJSON(Annotate.canvas.getActiveObject());
			if (after && JSON.stringify(after) !== JSON.stringify(before)) {
				Annotate.addUndo({
					id: after.annotationId,
					before: before,
					after: after
				});
			}
			let xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
			Annotate.mouseTo.x = xy.x;
			Annotate.mouseTo.y = xy.y;
			Annotate.drawingObject = null;
			Annotate.moveCount = 1;
			Annotate.doDrawing = false;
			if (canvas.isDrawingMode) {
				xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
				Annotate.notifyPenEvent({
					annotationId: Annotate.penId,
					event: 'mouseup'
				});
				let len = Annotate.canvas.getObjects().length;
				let item = Annotate.canvas.item(len - 1);
				item.annotationId = Annotate.penId;
				item.annotationType = 'path';
				Annotate.notifyAddItem(item);
				Annotate.addUndo({
					id: item.annotationId,
					before: null,
					after: Annotate.toJSON(item)
				});
			}
		});

		if (copyFrom) {
			canvas.loadFromJSON(copyFrom.canvas.toJSON(['annotationId', 'annotationType']));
			copyFrom.release();
		}
	}

	release() {
		this.canvas.clear();
		this.canvas.dispose();
		$(this.canvas.wrapperEl).remove();
		this.canvas['__eventListeners'] = {};
	}

	static movingComment(obj) {
		if (obj.annotationType === 'lead' || obj.annotationType === 'path' || obj.annotationType === 'rect' || obj.annotationType === 'image' || obj.annotationType === 'circle') {
			$.each(Annotate.canvas.getObjects(), (index, object) => {
				if ('comment' + obj.annotationId === object.annotationId) {
					let newLeft = obj.left;
					let newTop = obj.top;
					let distanceLeft = newLeft - Annotate.oldLeft;
					let distanceTop = newTop - Annotate.oldTop;
					object.set('left', Annotate.commentLeft + distanceLeft);
					object.set('top', Annotate.commentTop + distanceTop);
					object.setCoords();
					Annotate.notifyUpdateItem({
						annotationId: object.annotationId,
						annotationType: object.annotationType,
						left: object.left,
						top: object.top
					});
					Annotate.notifyLatestItem(object);
					Annotate.canvas.bringForward(object);
					Annotate.canvas.requestRenderAll();
					return false;
				}
			});
		}
	}

	static drawing() {
		let obj = Annotate.drawingObject;
		let canvas = Annotate.canvas;
		if (obj) {
			canvas.remove(obj);
			Annotate.notifyRemoveItem(obj);
			if (Annotate.undo) {
				Annotate.undoOperation.pop();
				Annotate.updateUndoRedoCss();
			}
			Annotate.undo = true;
		}
		let canvasObject = null;
		switch (Annotate.activeTool) {
			case Annotate.ActiveTool.Circle:
				canvasObject = Annotate.createCircle(Annotate.mouseFrom, Annotate.mouseTo);
				Annotate.notifyAddItem(canvasObject);
				canvas.selection = false;
				break;
			case Annotate.ActiveTool.Rectangle:
				canvasObject = Annotate.createRect(Annotate.mouseFrom, Annotate.mouseTo);
				Annotate.notifyAddItem(canvasObject);
				canvas.selection = false;
				break;
			default:
				break;
		}
		Annotate.drawingObject = canvasObject;
	}

	static waitLoaded() {
		return new Promise(resolve => {
			if (Annotate.canvas) {
				resolve();
			} else {
				let interval = setInterval(() => {
					if (Annotate.canvas) {
						resolve();
						clearInterval(interval);
					}
				}, 20);
			}
		});
	}

	static async processOneUpdateMessage() {
		Annotate.processingMessage = true;
		if (Annotate.messages.length === 0) {
			Annotate.processingMessage = false;
			return;
		}
		let data = null;
		Annotate.messages.some((item, index) => {
			if (item.page === Annotate.page) {
				data = Annotate.messages.splice(index, 1)[0];
				return true;
			}
		});
		if (data === null) {
			Annotate.processingMessage = false;
			return;
		}
		await Annotate.waitLoaded();
		let item = data.item;
		let action = data.action;
		Annotate.undo = false;
		switch (action) {
			case 'pen':
				let brush;
				let options = {
					e: {
						isPrimary: true
					}
				};
				switch (data.item.event) {
					case 'mousedown':
						brush = new fabric.PencilBrush(Annotate.canvas);
						brush.color = item.color;
						brush.width = item.borderSize;
						Annotate.brushs[data.id] = brush;
						brush.onMouseDown(item.point, options);
						break;
					case 'mousemove':
						brush = Annotate.brushs[data.id];
						if (brush) {
							brush.onMouseMove(item.point, options);
						}
						break;
					case 'mouseup':
						brush = Annotate.brushs[data.id];
						if (brush) {
							brush.onMouseUp(options);
							let len = Annotate.canvas.getObjects().length;
							Annotate.canvas.item(len - 1).annotationId = item.annotationId;
							Annotate.canvas.item(len - 1).annotationType = 'path';
						}
						break;
					default:
						break;
				}
				break;
			case 'add':
				if (data.storage) {
					Annotate.storages[item.annotationId] = item;
				}
				let exist = false;
				$.each(Annotate.canvas.getObjects(), (index, obj) => {
					if (obj.annotationId === item.annotationId) {
						$.each(item, (key, val) => {
							if (obj[key] !== val) {
								obj.set(key, val);
							}
						});
						exist = true;
						return false;
					}
				});
				if (!exist) {
					switch (item.annotationType) {
						case 'path':
							Annotate.createPath(item);
							break;
						case 'comment':
							Annotate.createText(item);
							break;
						case 'text':
							Annotate.createText(item);
							break;
						case 'lead':
							Annotate.createLeadWithOptions(item);
							break;
						case 'rect':
							Annotate.createRectWithOptions(item);
							break;
						case 'circle':
							Annotate.createCircleWithOptions(item);
							break;
						case 'image':
							await Annotate.createImage(item);
							break;
						default:
							break;
					}
				}
				break;
			case 'update':
				$.each(Annotate.canvas.getObjects(), (index, obj) => {
					if (item.annotationId === obj.annotationId) {
						$.each(item, (key, val) => {
							if (obj[key] !== val) {
								obj.set(key, val);
							}
						});
						Annotate.canvas.requestRenderAll();
						return false;
					}
				});
				break;
			case 'remove':
				$.each(Annotate.canvas.getObjects(), (index, obj) => {
					if (item.annotationId === obj.annotationId) {
						Annotate.canvas.remove(obj);
						return false;
					}
				});
				break;
			default:
				break;
		}
		Annotate.processOneUpdateMessage();
	}

	static processUpdateMessage() {
		if (Annotate.processingMessage) {
			return false;
		}
		Annotate.processOneUpdateMessage();
		if (Annotate.messages.length === 0) {
			return true;
		}
		return false;
	}

	static onUpdate(data) {
		Annotate.messages.push(data);
		Annotate.processUpdateMessage();
	}

	static updatePage(canvas, page) {
		Annotate.canvas = canvas;
		Annotate.page = page;
		Annotate.processUpdateMessage();
		// 更新toolbar状态
		Annotate.updateToobar();
	}

	static updateToobar() {
		// 设置hand选中
		$('#toolButtonHand').click();
	}

	static notifyItemAction(action, item, renew, clear, deliver, storage) {
		try {
			let update = {
				action: action,
				id: item.annotationId,
				item: item.toJSON ? Annotate.toJSON(item) : item,
				renew: renew,
				clear: clear
			};
			update.page = Annotate.page;
			if (deliver) {
				update.deliver = deliver;
			}
			if (storage) {
				update.storage = storage;
			} else if (update.item && update.item.annotationType === 'image') {
				delete update.item.src;
			}
			window.notifyUpdate(update);
		} catch (e) {
			console.log(e);
		}
	}

	static notifyPenEvent(item) {
		Annotate.notifyItemAction('pen', item, true, true);
	}

	static notifyAddItem(item) {
		Annotate.notifyItemAction('add', item, false, false, 'all', true);
	}

	static notifyLatestItem(item) {
		Annotate.notifyItemAction('update', item, true, false, 'ready');
	}

	static notifyRemoveItem(item) {
		Annotate.notifyItemAction('remove', item, false, true);
	}

	static notifyUpdateItem(item) {
		Annotate.notifyItemAction('update', item, true, false);
	}

	static updateActiveTool(pointerX, pointerY) {
		let canvas = Annotate.canvas;
		if (Annotate.activeTool === Annotate.ActiveTool.Text) {
			canvas.isDrawingMode = false;
			if (canvas.freeDrawingBrush.color === 'rgba(0, 0, 0, 0)') {
				Annotate.setColor('black');
			}

			let text = Annotate.createText({
				left: pointerX,
				top: pointerY,
				fill: Annotate.color,
				fontSize: Annotate.fontSize,
				selectable: true,
				annotationId: Annotate.uuid(),
				annotationType: 'text'
			});
			Annotate.notifyAddItem(text);

			Annotate.activeTool = Annotate.ActiveTool.Selector;
			Annotate.activeToolButton(Annotate.ActiveTool.Selector);
		} else if (Annotate.activeTool === Annotate.ActiveTool.Lead) {
			let lead = Annotate.createLead(pointerX, pointerY);
			Annotate.notifyAddItem(lead);
		}
	}

	static createPath(options) {
		let path = new fabric.Path(options.path, options);
		Annotate.canvas.add(path);
		return path;
	}

	static createText(options) {
		let text = new fabric.Textbox(options.text ? options.text : '添加文字', options);
		Annotate.canvas.add(text);
		let before = Annotate.toJSON(text);
		text.on('editing:exited', () => {
			Annotate.addUndo({
				id: text.annotationId,
				before: before,
				after: Annotate.toJSON(text)
			});
		});
		text.on('changed', () => {
			$('.controlPanel').remove();
			text.set('fill', Annotate.color);
			text.set('fontSize', Annotate.fontSize);
			Annotate.notifyUpdateItem({
				annotationId: text.annotationId,
				annotationType: text.annotationType,
				text: text.text
			});
			Annotate.notifyLatestItem(text);
		});
		text.on('mousedblclick', () => {
			before = Annotate.toJSON(text);
			text.enterEditing();
			text.selectAll();
		});
		text.on('mouseout', () => {});
		text.on('mouseon', () => {
			Annotate.mouseOutFlag = false;
		});
		return text;
	}

	static createLeadWithOptions(options) {
		let canvas = Annotate.canvas;
		let lead = new fabric.Path('M 0 30 L 30 0 M 30 0 L 130 0 M 0 30 L 5 20  M 0 30 L 10 25 L 5 20', options);
		canvas.add(lead);
		return lead;
	}

	static createLead(left, top) {
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
		let leadBordersize = Annotate.borderSize >= 2 ? 2 : 1;
		let lead = Annotate.createLeadWithOptions({
			annotationId: Annotate.uuid(),
			annotationType: 'lead',
			left: left,
			top: top,
			fill: Annotate.borderColor,
			stroke: Annotate.borderColor,
			strokeWidth: leadBordersize
		});
		return lead;
	}

	static createCircleWithOptions(options) {
		let circle = new fabric.Circle(options);
		Annotate.canvas.add(circle);
		return circle;
	}

	static createCircle(mouseFrom, mouseTo) {
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
		let left = mouseFrom.x,
			top = mouseFrom.y;
		let radius = Math.sqrt((mouseTo.x - left) * (mouseTo.x - left) + (mouseTo.y - top) * (mouseTo.y - top)) / 2;
		let circle = Annotate.createCircleWithOptions({
			left: left,
			top: top,
			fill: Annotate.fillColor,
			stroke: Annotate.borderColor,
			radius: radius,
			strokeWidth: Annotate.borderSize,
			annotationId: Annotate.uuid(),
			annotationType: 'circle'
		});
		return circle;
	}

	static createRectWithOptions(options) {
		let rect = new fabric.Rect(options);
		Annotate.canvas.add(rect);
		return rect;
	}

	static createRect(mouseFrom, mouseTo) {
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
		let left = mouseFrom.x,
			top = mouseFrom.y;
		let width = mouseTo.x - left,
			height = mouseTo.y - top;
		let rect = Annotate.createRectWithOptions({
			left: left,
			top: top,
			width: width,
			height: height,
			fill: Annotate.fillColor,
			stroke: Annotate.borderColor,
			strokeWidth: Annotate.borderSize,
			annotationId: Annotate.uuid(),
			annotationType: 'rect'
		});
		return rect;
	}

	static createImage(options) {
		return new Promise(resolve => {
			let image = new Image();
			if (options.annotationId && Annotate.storages[options.annotationId]) {
				image.src = Annotate.storages[options.annotationId].src;
				delete Annotate.storages[options.annotationId];
			} else {
				image.src = options.src;
			}

			image.onload = function () {
				let img = new fabric.Image(image, options);
				Annotate.canvas.add(img);
				resolve(img);
			};
		});
	}

	static enableSelector() {
		Annotate.activeTool = Annotate.ActiveTool.Selector;
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
	}

	static enableAddText() {
		Annotate.activeTool = Annotate.ActiveTool.Text;
		Annotate.activeToolButton(Annotate.ActiveTool.Text);
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
	}

	static enablePencil() {
		Annotate.activeTool = Annotate.ActiveTool.Pencil;
		Annotate.activeToolButton(Annotate.ActiveTool.Pencil);

		if (Annotate.canvas) {
			Annotate.canvas.freeDrawingBrush.width = Annotate.borderSize;
			Annotate.canvas.isDrawingMode = true;
			if (Annotate.canvas.freeDrawingBrush.color === 'rgba(0, 0, 0, 0)') {
				Annotate.setColor('black');
			}
		}
	}

	static enableAddMiddleText(left, top, text, canvas, object) {
		let exist = false;
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if ('comment' + object.annotationId === obj.annotationId) {
				let before = Annotate.toJSON(obj);
				obj.text = text;
				obj.fill = Annotate.color;
				obj.fontSize = Annotate.fontSize;
				Annotate.addUndo({
					id: obj.annotationId,
					before: before,
					after: Annotate.toJSON(obj)
				});
				Annotate.notifyUpdateItem({
					annotationId: obj.annotationId,
					annotationType: obj.annotationType,
					text: obj.text
				});
				Annotate.notifyLatestItem(obj);
				exist = true;
			}
		});
		if (!exist) {
			let textItem = Annotate.createText({
				left: left,
				top: top,
				text: text,
				fill: Annotate.color,
				fontSize: Annotate.fontSize,
				selectable: true,
				annotationType: 'comment',
				annotationId: 'comment' + object.annotationId
			});
			textItem.left = textItem.left - textItem.width / 2;
			textItem.top = textItem.top - textItem.height / 2;
			Annotate.notifyAddItem(textItem);
		}
		Annotate.renderAll();
		Annotate.activeTool = Annotate.ActiveTool.Selector;
		Annotate.activeToolButton(Annotate.ActiveTool.Selector);
	}

	static toObject(obj) {
		if (obj) {
			return obj.toObject(['annotationId', 'annotationType']);
		}
		return null;
	}

	static toJSON(obj) {
		if (obj) {
			return obj.toJSON(['annotationId', 'annotationType']);
		}
		return null;
	}

	static createControlPanel(canvas, object) {
		if (object.annotationType !== 'text' && object.annotationType !== 'comment') {
			$('.controlPanel').remove();
			$(
				'<div class="controlPanel" id="' +
					object.annotationId +
					'" style="display: block;">' +
					'<div class="controlPanel-delete">' +
					'<i class="fa fa-trash-o" aria-hidden="true"></i>' +
					'</div>' +
					'<div class="controlPanel-conment">' +
					'<i class="fa fa-comment-o" aria-hidden="true"></i>' +
					'</div></div>'
			).appendTo($('.canvas-container'));
			let objWidth = object.width * object.scaleX;
			if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
				$('.controlPanel').css('left', object.left * Annotate.scale - 35);
			} else {
				$('.controlPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
			}
			$('.controlPanel').css('top', object.top * Annotate.scale - 5);
		} else if (object.annotationType === 'text') {
			$('.controlPanel').remove();
			$(
				'<div class="controlPanel" id="controlPanel' +
					object.annotationId +
					'" style="display: block;">' +
					'<div class="controlPanel-delete">' +
					'<i class="fa fa-trash-o" aria-hidden="true"></i>' +
					'</div></div>'
			).appendTo($('.canvas-container'));
			let objWidth = object.width * object.scaleX;
			if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
				$('.controlPanel').css('left', object.left * Annotate.scale - 35);
			} else {
				$('.controlPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
			}
			$('.controlPanel').css('top', object.top * Annotate.scale - 5);
		}
	}

	static createCommentPanel(canvas, object) {
		$('.conmentPanel').remove();
		$(
			'<div class="conmentPanel" id=conmentPanel' +
				object.annotationId +
				' style="display: block;">' +
				'<textarea></textarea>' +
				'<div class="conmentPanel-bottom">' +
				'<div id="conmentPanel-bottom-save">' +
				'<i class="fa fa-check fa-lg" aria-hidden="true"></i>' +
				'</div>' +
				'<div id="conmentPanel-bottom-cancel">' +
				'<i class="fa fa-times fa-lg" aria-hidden="true"></i>' +
				'</div>' +
				'</div></div>'
		).appendTo($('.canvas-container'));
		let objWidth = object.width * object.scaleX;
		if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
			$('.conmentPanel').css('left', object.left * Annotate.scale - 205);
		} else {
			$('.conmentPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
		}
		$('.conmentPanel').css('top', object.top * Annotate.scale);
	}

	static deleteSelectedObject() {
		let canvas = Annotate.canvas;
		let activeObject = Annotate.canvas.getActiveObject();
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if ('comment' + activeObject.annotationId === obj.annotationId) {
				Annotate.canvas.remove(obj);
				Annotate.notifyRemoveItem(obj);
			}
		});

		if (activeObject['_objects'] !== undefined) {
			let etCount = activeObject['_objects'].length;
			for (let etindex = 0; etindex < etCount; etindex++) {
				canvas.remove(activeObject['_objects'][etindex]);
			}

			if (activeObject.annotationType === 'lead') {
				canvas.remove(activeObject);
			}
			canvas.discardActiveObject();
		} else {
			canvas.remove(activeObject);
		}
		canvas.requestRenderAll();
		Annotate.updateUndoRedoCss();
		Annotate.notifyRemoveItem(activeObject);
		return activeObject;
	}

	static setBrushSize(size) {
		if (Annotate.canvas) {
			Annotate.canvas.freeDrawingBrush.width = size;
		}
	}

	static setColor(color) {
		Annotate.color = color;
		if (Annotate.canvas) {
			Annotate.canvas.freeDrawingBrush.color = color;
		}
	}

	static setBorderColor(color) {
		Annotate.borderColor = color;
	}

	static setFontSize(size) {
		Annotate.fontSize = size;
		Annotate.canvas.discardActiveObject();
		Annotate.renderAll();
	}

	static setBorderSize(size) {
		Annotate.borderSize = size;
	}

	static loadFromJSON(jsonData) {
		if (Annotate.canvas) {
			Annotate.canvas.loadFromJSON(jsonData, () => {});
			Annotate.renderAll();
		}
	}

	static addImageToCanvas() {
		let canvas = Annotate.canvas;
		Annotate.activeTool = Annotate.ActiveTool.Image;
		if (canvas) {
			let inputElement = document.createElement('input');
			inputElement.type = 'file';
			inputElement.accept = 'image/*';
			inputElement.onchange = () => {
				let reader = new FileReader();
				reader.addEventListener(
					'load',
					() => {
						inputElement.remove();
						let id = Annotate.uuid();
						Annotate.notifyAddItem({
							annotationType: 'image',
							annotationId: id,
							src: reader.result
						});
						Annotate.createImage({
							annotationType: 'image',
							annotationId: id,
							src: reader.result
						});
					},
					false
				);
				reader.readAsDataURL(inputElement.files[0]);
			};
			inputElement.click();
		}
	}

	static renderAll() {
		Annotate.canvas.renderAll();
	}

	static uuid() {
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
			let r = (Math.random() * 16) | 0,
				v = c === 'x' ? r : (r & 0x3) | 0x8;
			return v.toString(16);
		});
	}

	static async recoveryDeleteItem(item) {
		let obj = null;
		Annotate.undo = false;
		if (item.annotationType === 'text') {
			obj = Annotate.createText(item);
		} else if (item.annotationType === 'path') {
			obj = Annotate.createPath(item);
		} else if (item.annotationType === 'rect') {
			obj = Annotate.createRectWithOptions(item);
		} else if (item.annotationType === 'circle') {
			obj = Annotate.createCircleWithOptions(item);
		} else if (item.annotationType === 'image') {
			obj = await Annotate.createImage(item);
		} else if (item.annotationType === 'lead') {
			obj = Annotate.createLeadWithOptions(item);
		} else if (item.annotationType === 'comment') {
			obj = Annotate.createText(item);
			obj.left = item.left - obj.width / 2;
			obj.top = item.top - obj.height / 2;
		} else {
			Annotate.undo = true;
			console.log('cannot recover item type:', item.annotationType);
		}
		if (obj) {
			Annotate.notifyAddItem(obj);
		}
	}

	static addUndo(step) {
		Annotate.undoOperation.push(step);
		Annotate.updateUndoRedoCss();
	}

	static addRedo(step) {
		Annotate.redoOperation.push(step);
		Annotate.updateUndoRedoCss();
	}

	static updateUndoRedoCss() {
		if (Annotate.undoOperation.length <= 0) {
			$('#undoButton i').css('color', 'white');
		} else {
			$('#undoButton i').css('color', 'black');
		}

		if (Annotate.redoOperation.length <= 0) {
			$('#redoButton i').css('color', 'white');
		} else {
			$('#redoButton i').css('color', 'black');
		}
	}

	static activeToolButton(buttonIndex) {
		$('.toolbar .tool .tool-button').attr('class', 'tool-button');
		$('.toolbar .tool .tool-button').eq(buttonIndex).attr('class', 'tool-button active');
	}

	static transformMouse(mouseX, mouseY) {
		return {
			x: mouseX / Annotate.scale,
			y: mouseY / Annotate.scale
		};
	}

	static setZoom(scale) {
		Annotate.scale = scale;
		if (Annotate.canvas) {
			Annotate.canvas.setZoom(scale);
		}
	}

	static setDimensions(dimensions) {
		if (Annotate.canvas) {
			Annotate.canvas.setDimensions(dimensions);
		}
	}
}

if (Annotate.hasBordersAndControls) {
	fabric.Object.prototype.set(Annotate.bordersAndControlsOptions);
} else {
	fabric.Object.prototype.set({
		hasControls: false,
		hasBorders: false
	});
}

function changeActiveTool(event) {
	let element = $(event.target).hasClass('tool-button') ? $(event.target) : $(event.target).parents('.tool-button').first();
	$('.tool-button.active').removeClass('active');
	$(element).addClass('active');
}

// eslint-disable-next-line no-unused-vars
function enablePencil(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.enablePencil();
}

// eslint-disable-next-line no-unused-vars
function enableAddText(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.enableAddText();
}

// eslint-disable-next-line no-unused-vars
function enableSelector(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.enableSelector();
}

// eslint-disable-next-line no-unused-vars
function enableRectangle(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.activeTool = Annotate.ActiveTool.Rectangle;
	if (Annotate.canvas) {
		Annotate.canvas.isDrawingMode = false;
	}
}

// eslint-disable-next-line no-unused-vars
function enableCircle(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.activeTool = Annotate.ActiveTool.Circle;
	if (Annotate.canvas) {
		Annotate.canvas.isDrawingMode = false;
	}
}

// eslint-disable-next-line no-unused-vars
function enableLead(event) {
	event.preventDefault();
	changeActiveTool(event);
	Annotate.activeTool = Annotate.ActiveTool.Lead;
	if (Annotate.canvas) {
		Annotate.canvas.isDrawingMode = false;
	}
}

// eslint-disable-next-line no-unused-vars
async function undo() {
	Annotate.enableSelector();
	if (Annotate.undoOperation.length === 0) {
		$('#undoButton i').css('color', 'white');
	} else {
		$('#undoButton').css('cursor', 'pointer');
		let currentOperation = Annotate.undoOperation.pop();
		Annotate.addRedo(currentOperation);
		Annotate.canvas.discardActiveObject();
		let exist = false;
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if (currentOperation.id === obj.annotationId) {
				exist = true;
				if (currentOperation.before === null) {
					Annotate.canvas.setActiveObject(obj);
					Annotate.undo = false;
					Annotate.deleteSelectedObject();
				} else {
					Annotate.undo = false;
					let updateData = {
						annotationId: obj.annotationId,
						annotationType: obj.annotationType
					};
					$.each(currentOperation.before, (key, val) => {
						if (obj[key] !== val) {
							obj.set(key, val);
							updateData[key] = val;
						}
					});
					Annotate.movingComment(obj);
					Annotate.notifyUpdateItem(updateData);
					Annotate.notifyLatestItem(obj);
					Annotate.canvas.setActiveObject(obj);
				}
				return false;
			}
		});
		if (!exist && currentOperation.after === null) {
			Annotate.recoveryDeleteItem(currentOperation.before);
		} else if (!exist) {
			console.log('unknow operation', currentOperation);
		}
	}
	Annotate.canvas.requestRenderAll();
}

// eslint-disable-next-line no-unused-vars
async function redo() {
	Annotate.enableSelector();
	if (Annotate.redoOperation.length === 0) {
		$('#redoButton i').css('color', 'white');
	} else {
		$('#undoButton').css('cursor', 'pointer');
		let currentOperation = Annotate.redoOperation.pop();
		Annotate.addUndo(currentOperation);
		Annotate.canvas.discardActiveObject();
		let exist = false;
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if (currentOperation.id === obj.annotationId) {
				exist = true;
				if (currentOperation.after === null) {
					Annotate.canvas.setActiveObject(obj);
					Annotate.deleteSelectedObject();
				} else {
					Annotate.undo = false;
					let updateData = {
						annotationId: obj.annotationId,
						annotationType: obj.annotationType
					};
					$.each(currentOperation.after, (key, val) => {
						if (obj[key] !== val) {
							obj.set(key, val);
							updateData[key] = val;
						}
					});
					Annotate.movingComment(obj);
					Annotate.notifyUpdateItem(updateData);
					Annotate.notifyLatestItem(obj);
					Annotate.canvas.setActiveObject(obj);
				}
				return false;
			}
		});
		if (!exist && currentOperation.before === null) {
			Annotate.recoveryDeleteItem(currentOperation.after);
		} else if (!exist) {
			console.log('unknow operation', currentOperation);
		}
		Annotate.canvas.requestRenderAll();
	}
}

// eslint-disable-next-line no-unused-vars
function addImage(event) {
	event.preventDefault();
	Annotate.addImageToCanvas();
}

$(() => {
	$('#brush-size').change(() => {
		let width = $('#brush-size').val();
		Annotate.setBrushSize(width);
		Annotate.setBorderSize(parseInt(width));
	});
	$('#brush-size').keyup(function () {
		var value = $('#brush-size').val();
		value = value.replace(/[^0-9\.]/g, '');
		if (value < 1) {
			value = 1;
		}
		if (value > 50) {
			value = 50;
		}
		$('#brush-size').val(value);
	});

	$('#brush-size').on('focus', () => {
		if (Annotate.canvas) {
			Annotate.canvas.isDrawingMode = false;
		}
		Annotate.activeTool = Annotate.ActiveTool.Selector;
		Annotate.activeToolButton(Annotate.ActiveTool.Selector);
	});

	$('#font-size').change(() => {
		let fontSize = $('#font-size').val();
		Annotate.setFontSize(fontSize);
	});

	$('body').on('click', '.controlPanel-delete', event => {
		event.preventDefault();
		let obj = Annotate.deleteSelectedObject();
		Annotate.addUndo({
			id: obj.annotationId,
			before: obj,
			after: null
		});
	});

	$('body').on('click', '.controlPanel-conment', () => {
		let commentPanelId = $('.controlPanel').attr('id');
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if (commentPanelId === obj.annotationId) {
				Annotate.createCommentPanel(Annotate.canvas, obj);
			}
		});
	});

	$('body').on('click', '.controlPanel-conment', () => {
		let commentPanelId = $('.controlPanel').attr('id');
		$.each(Annotate.canvas.getObjects(), (index, obj) => {
			if (commentPanelId === obj.annotationId) {
				Annotate.createCommentPanel(Annotate.canvas, obj);
			}
		});
	});

	$('body').on('click', '#conmentPanel-bottom-cancel', () => {
		$('.conmentPanel').remove();
	});

	$('body').on('click', '#conmentPanel-bottom-save', () => {
		let text = $('.conmentPanel textarea').val();
		if (text !== '') {
			let commentPanelId = $('.controlPanel').attr('id');

			$.each(Annotate.canvas.getObjects(), (index, obj) => {
				if (commentPanelId === obj.annotationId) {
					let objLeft = obj.left + (obj.width * obj.scaleX) / 2;
					let objTop = obj.top + (obj.height * obj.scaleY) / 2;
					Annotate.enableAddMiddleText(objLeft, objTop, text, Annotate.canvas, obj);
				}
			});
			Annotate.canvas.discardActiveObject();
		}

		$('.conmentPanel').remove();
	});

	$('.color-plate').hide();
	$('.middle').hide();

	$('#undoButton').mouseover(() => {
		if (Annotate.undoOperation <= 0) {
			$('#undoButton').css('cursor', 'not-allowed');
		} else {
			$('#undoButton').css('cursor', 'pointer');
		}
	});
	$('#redoButton').mouseover(() => {
		if (Annotate.redoOperation <= 0) {
			$('#redoButton').css('cursor', 'not-allowed');
		} else {
			$('#redoButton').css('cursor', 'pointer');
		}
	});

	$('body').on('click', '.color-plate-item', e => {
		let backgroundColor = $(e.currentTarget).css('background-color');
		$('.toolbar-button-drop-button').css('background-color', backgroundColor);
		Annotate.setBorderColor(backgroundColor);
		Annotate.setColor(backgroundColor);
		$('.color-plate').toggle(200);
		let activeObject = Annotate.canvas.getActiveObject();
		if (activeObject) {
			let before = Annotate.toJSON(activeObject);
			if (activeObject.annotationType === 'image') {
				return;
			} else if (activeObject.annotationType === 'comment' || activeObject.annotationType === 'text') {
				activeObject.set('fill', backgroundColor);
				let after = Annotate.toJSON(activeObject);
				if (JSON.stringify(before) !== JSON.stringify(after)) {
					Annotate.addUndo({
						id: after.annotationId,
						before: before,
						after: after
					});
					Annotate.notifyUpdateItem({
						annotationId: after.annotationId,
						annotationType: after.annotationType,
						fill: after.fill
					});
					Annotate.notifyLatestItem(activeObject);
				}
				Annotate.renderAll();
			} else {
				activeObject.set('stroke', backgroundColor);
				let after = Annotate.toJSON(activeObject);
				if (JSON.stringify(before) !== JSON.stringify(after)) {
					Annotate.addUndo({
						id: after.annotationId,
						before: before,
						after: after
					});
					Annotate.notifyUpdateItem({
						annotationId: after.annotationId,
						annotationType: after.annotationType,
						stroke: after.stroke
					});
					Annotate.notifyLatestItem(activeObject);
				}
				Annotate.renderAll();
			}
		}
	});

	$('.toolbar-button-drop-button').click(() => {
		$('.color-plate').toggle(200);
	});
});
